Alink漫談(六) : TF-IDF算法的實現

Alink漫談(六) : TF-IDF算法的實現

目錄

  • Alink漫談(六) : TF-IDF算法的實現
    • 0x00 摘要
    • 0x01 TF-IDF
      • 1.1 原理
      • 1.2 計算方法
    • 0x02 Alink示例代碼
      • 2.1 示例代碼
      • 2.2 TF-IDF模型
      • 2.3 TF-IDF預測
    • 0x03 分詞 Segment
      • 3.1 結巴分詞
      • 3.2 分詞過程
    • 0x04 訓練
      • 4.1 計算IDF
      • 4.2 排序
        • 4.2.1 SortUtils.pSort
          • 採樣SampleSplitPoint
          • 歸併 SplitPointReducer
          • SplitData把真實數據IDF插入
          • reduceGroup計算同類型單詞數目
        • 4.2.2 localSort
      • 4.3 過濾
    • 0x05 生成模型
      • 5.1 DocCountVectorizerModelData
      • 5.2 BuildDocCountModel
    • 0x06 預測
    • 0x07 參考

0x00 摘要

Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習算法平台,是業界首個同時支持批式算法、流式算法的機器學習平台。TF-IDF(term frequency–inverse document frequency)是一種用於信息檢索與數據挖掘的常用加權技術。本文將為大家展現Alink如何實現TF-IDF。

0x01 TF-IDF

TF-IDF(term frequency–inverse document frequency)是一種統計方法,一種用於信息檢索與數據挖掘的常用加權技術。

TF是詞頻(Term Frequency),IDF是逆文本頻率指數(Inverse Document Frequency)。

為什麼要用TF-IDF?因為計算機只能識別数字,對於一個一個的單詞,計算機是看不懂的,更別說是一句話,或是一篇文章。而TF-IDF就是用來將文本轉換成計算機看得懂的語言,或者說是機器學習或深度學習模型能夠進行學習訓練的數據集

1.1 原理

TF-IDF用以評估一個詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。

TF-IDF的主要思想是:如果某個詞或短語在一篇文章中出現的頻率TF高,並且在其他文章中很少出現,則認為此詞或者短語具有很好的類別區分能力,適合用來分類。

TF-IDF實際上是:TF * IDF,TF詞頻(Term Frequency),IDF逆向文件頻率(Inverse Document Frequency)。

詞頻(term frequency,TF)指的是某一個給定的詞語在該文件中出現的頻率。這個数字是對詞數(term count)的歸一化,以防止它偏向長的文件(同一個詞語在長文件里可能會比短文件有更高的詞數,而不管該詞語重要與否)。

IDF逆向文件頻率 (inverse document frequency, IDF)反應了一個詞在所有文本(整個文檔)中出現的頻率,如果一個詞在很多的文本中出現,那麼它的IDF值應該低。而反過來如果一個詞在比較少的文本中出現,那麼它的IDF值應該高。比如一些專業的名詞如“Machine Learning”。這樣的詞IDF值應該高。一個極端的情況,如果一個詞在所有的文本中都出現,那麼它的IDF值應該為0。

如果單單以TF或者IDF來計算一個詞的重要程度都是片面的,因此TF-IDF綜合了TF和IDF兩者的優點,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨着它在文件中出現的次數成正比增加,但同時會隨着它在語料庫中出現的頻率成反比下降。上述引用總結就是:一個詞語在一篇文章中出現次數越多, 同時在所有文檔中出現次數越少, 越能夠代表該文章,越能與其它文章區分開來。

1.2 計算方法

TF的計算公式如下:

\[TF_w = \frac {N_w}{N} \]

其中 N_w 是在某一文本中詞條w出現的次數,N 是該文本總詞條數。

IDF的計算公式如下:

\[IDF_w = log(\frac {Y}{Y_w + 1}) \]

其中 Y 是語料庫的文檔總數,Y_w 是包含詞條w的文檔數,分母加一是為了避免w 未出現在任何文檔中從而導致分母為0 的情況。

TF-IDF 就是將TF和IDF相乘 :

\[TF-IDF_w = TF_w * IDF_w \]

從以上計算公式便可以看出,某一特定文件內的高詞語頻率,以及該詞語在整個文件集合中的低文件頻率,可以產生出高權重的TF-IDF。因此,TF-IDF傾向於過濾掉常見的詞語,保留重要的詞語。

0x02 Alink示例代碼

2.1 示例代碼

首先我們給出示例代碼,下文是通過一些語料來訓練出一個模型,然後用這個模型來做預測:

public class DocCountVectorizerExample {

    AlgoOperator getData(boolean isBatch) {
        Row[] rows = new Row[]{
                Row.of(0, "二手舊書:醫學電磁成像"),
                Row.of(1, "二手美國文學選讀( 下冊 )李宜燮南開大學出版社 9787310003969"),
                Row.of(2, "二手正版圖解象棋入門/謝恩思主編/華齡出版社"),
                Row.of(3, "二手中國糖尿病文獻索引"),
                Row.of(4, "二手郁達夫文集( 國內版 )全十二冊館藏書")
        };

        String[] schema = new String[]{"id", "text"};

        if (isBatch) {
            return new MemSourceBatchOp(rows, schema);
        } else {
            return new MemSourceStreamOp(rows, schema);
        }
    }

    public static void main(String[] args) throws Exception {
        DocCountVectorizerExample test = new DocCountVectorizerExample();
        BatchOperator batchData = (BatchOperator) test.getData(true);

         // 分詞
        SegmentBatchOp segment = new SegmentBatchOp() 
                                                .setSelectedCol("text")
                                                .linkFrom(batchData);
        // TF-IDF訓練
        DocCountVectorizerTrainBatchOp model = new DocCountVectorizerTrainBatchOp()
                                                .setSelectedCol("text")
                                                .linkFrom(segment);
        // TF-IDF預測
        DocCountVectorizerPredictBatchOp predictBatch = new 
            																		DocCountVectorizerPredictBatchOp()
                                                .setSelectedCol("text")
                                                .linkFrom(model, segment);
        model.print();
        predictBatch.print();
    }
}

2.2 TF-IDF模型

TF-IDF模型打印出來如下:

model_id|model_info
--------|----------
0|{"minTF":"1.0","featureType":"\"WORD_COUNT\""}
1048576|{"f0":"二手","f1":0.0,"f2":0}
2097152|{"f0":"/","f1":1.0986122886681098,"f2":1}
3145728|{"f0":"出版社","f1":0.6931471805599453,"f2":2}
4194304|{"f0":")","f1":0.6931471805599453,"f2":3}
5242880|{"f0":"(","f1":0.6931471805599453,"f2":4}
6291456|{"f0":"入門","f1":1.0986122886681098,"f2":5}
......
36700160|{"f0":"美國","f1":1.0986122886681098,"f2":34}
37748736|{"f0":"謝恩","f1":1.0986122886681098,"f2":35}
38797312|{"f0":"象棋","f1":1.0986122886681098,"f2":36}

2.3 TF-IDF預測

TF-IDF預測結果如下:

id|text
--|----
0|$37$0:1.0 6:1.0 10:1.0 25:1.0 26:1.0 28:1.0
1|$37$0:1.0 1:1.0 2:1.0 4:1.0 11:1.0 15:1.0 16:1.0 19:1.0 20:1.0 32:1.0 34:1.0
2|$37$0:1.0 3:2.0 4:1.0 5:1.0 8:1.0 22:1.0 23:1.0 24:1.0 29:1.0 35:1.0 36:1.0
3|$37$0:1.0 12:1.0 27:1.0 31:1.0 33:1.0
4|$37$0:1.0 1:1.0 2:1.0 7:1.0 9:1.0 13:1.0 14:1.0 17:1.0 18:1.0 21:1.0 30:1.0

0x03 分詞 Segment

中文分詞(Chinese Word Segmentation) 指的是將一個漢字序列切分成一個一個單獨的詞。分詞就是將連續的字序列按照一定的規範重新組合成詞序列的過程。

示例代碼中,分詞部分如下:

    SegmentBatchOp segment = new SegmentBatchOp() 
                                            .setSelectedCol("text")
                                            .linkFrom(batchData);

分詞主要是如下兩個類,其作用就是把中文文檔分割成單詞。

public final class SegmentBatchOp extends MapBatchOp <SegmentBatchOp>
	implements SegmentParams <SegmentBatchOp> {

	public SegmentBatchOp(Params params) {
		super(SegmentMapper::new, params);
	}
}

public class SegmentMapper extends SISOMapper {
	private JiebaSegmenter segmentor;
}

3.1 結巴分詞

有經驗的同學看到這裏就會露出微笑:結巴分詞。

jieba分詞是國內使用人數最多的中文分詞工具https://github.com/fxsjy/jieba。jieba分詞支持四種分詞模式:

  • 精確模式,試圖將句子最精確地切開,適合文本分析;
  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;
  • 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。
  • paddle模式,利用PaddlePaddle深度學習框架,訓練序列標註(雙向GRU)網絡模型實現分詞。

Alink使用了com.alibaba.alink.operator.common.nlp.jiebasegment.viterbi.FinalSeg;來 完成分詞。具體是在https://github.com/huaban/jieba-analysis的基礎上稍微做了調整。

public class JiebaSegmenter implements Serializable {
    private static FinalSeg finalSeg = FinalSeg.getInstance();
    private WordDictionary wordDict;
    ......
    private Map<Integer, List<Integer>> createDAG(String sentence) 
}

從Alink代碼中看,實現了索引分詞和查詢分詞兩種模式,應該是有分詞粒度粗細之分。

createDAG函數的作用是 :在處理句子過程中,基於前綴詞典實現高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構成的有向無環圖 (DAG)。

結巴分詞對於未登錄詞,採用了基於漢字成詞能力的 HMM 模型,使用了 Viterbi 算法。

3.2 分詞過程

分詞過程主要是在SegmentMapper.mapColumn函數中完成的,當輸入是 “二手舊書:醫學電磁成像”,結巴分詞將這個句子分成了六個單詞。具體參見如下:

input = "二手舊書:醫學電磁成像"
tokens = {ArrayList@9619}  size = 6
 0 = {SegToken@9630} "[二手, 0, 2]"
 1 = {SegToken@9631} "[舊書, 2, 4]"
 2 = {SegToken@9632} "[:, 4, 5]"
 3 = {SegToken@9633} "[醫學, 5, 7]"
 4 = {SegToken@9634} "[電磁, 7, 9]"
 5 = {SegToken@9635} "[成像, 9, 11]"
 
mapColumn:44, SegmentMapper (com.alibaba.alink.operator.common.nlp)
apply:-1, 35206803 (com.alibaba.alink.common.mapper.SISOMapper$$Lambda$646)
handleMap:75, SISOColsHelper (com.alibaba.alink.common.mapper)
map:52, SISOMapper (com.alibaba.alink.common.mapper)
map:21, MapperAdapter (com.alibaba.alink.common.mapper)
map:11, MapperAdapter (com.alibaba.alink.common.mapper)
collect:79, ChainedMapDriver (org.apache.flink.runtime.operators.chaining)
collect:35, CountingCollector (org.apache.flink.runtime.operators.util.metrics)
invoke:196, DataSourceTask (org.apache.flink.runtime.operators)

0x04 訓練

訓練是在DocCountVectorizerTrainBatchOp類完成的,其通過linkFrom完成了模型的構建。其實計算TF IDF相對 簡單,複雜之處在於之後的大規模排序。

public DocCountVectorizerTrainBatchOp linkFrom(BatchOperator<?>... inputs) {
        BatchOperator<?> in = checkAndGetFirst(inputs);
  
        DataSet<DocCountVectorizerModelData> resDocCountModel = generateDocCountModel(getParams(), in);

        DataSet<Row> res = resDocCountModel.mapPartition(new MapPartitionFunction<DocCountVectorizerModelData, Row>() {
            @Override
            public void mapPartition(Iterable<DocCountVectorizerModelData> modelDataList, Collector<Row> collector) {
                new DocCountVectorizerModelDataConverter().save(modelDataList.iterator().next(), collector);
            }
        });
        this.setOutput(res, new DocCountVectorizerModelDataConverter().getModelSchema());
        return this;
}

4.1 計算IDF

計算 IDF 的工作是在generateDocCountModel完成的,具體步驟如下:

第一步 通過DocWordSplitCount和UDTF的混合使用得到了文檔中的單詞數目docWordCnt

BatchOperator<?> docWordCnt = in.udtf(
        params.get(SELECTED_COL),
        new String[] {WORD_COL_NAME, DOC_WORD_COUNT_COL_NAME},
        new DocWordSplitCount(NLPConstant.WORD_DELIMITER),
        new String[] {});

DocWordSplitCount.eval的輸入是已經分詞的句子,然後按照空格分詞,按照單詞計數。其結果是:

map = {HashMap@9816}  size = 6
 "醫學" -> {Long@9833} 1
 "電磁" -> {Long@9833} 1
 ":" -> {Long@9833} 1
 "成像" -> {Long@9833} 1
 "舊書" -> {Long@9833} 1
 "二手" -> {Long@9833} 1

第二步 得到了文檔數目docCnt

BatchOperator docCnt = in.select("COUNT(1) AS " + DOC_COUNT_COL_NAME);

這個數目會廣播出去 .withBroadcastSet(docCnt.getDataSet(), "docCnt");,後面的CalcIdf會繼續使用,進行 行數統計。

第三步 會通過CalcIdf計算出每一個單詞的DF和IDF

open時候會獲取docCnt。然後reduce會計算IDF,具體計算如下:

double idf = Math.log((1.0 + docCnt) / (1.0 + df));
collector.collect(Row.of(featureName, -wordCount, idf));

具體得到如下

df = 1.0
wordCount = 1.0
featureName = "中國"
idf = 1.0986122886681098
docCnt = 5

這裏一個重點是:返回值中,是 -wordCount,因為單詞越多權重越小,為了比較所以取負

4.2 排序

得到所有單詞的IDF之後,就得到了一個IDF字典,這時候需要對字典按照權重進行排序。排序具體分為兩步。

4.2.1 SortUtils.pSort

第一步是SortUtils.pSort,大規模并行抽樣排序。

Tuple2<DataSet<Tuple2<Integer, Row>>, DataSet<Tuple2<Integer, Long>>> partitioned = SortUtils.pSort(sortInput, 1);

這步非常複雜,Alink參考了論文,如果有興趣的兄弟可以深入了解下。

* reference: Yang, X. (2014). Chong gou da shu ju tong ji (1st ed., pp. 25-29).
* Note: This algorithm is improved on the base of the parallel sorting by regular sampling(PSRS).

pSort返回值是:

* @return f0: dataset which is indexed by partition id, f1: dataset which has partition id and count.

pSort中又分如下幾步

採樣SampleSplitPoint

SortUtils.SampleSplitPoint.mapPartition這裏完成了採樣。

DataSet <Tuple2 <Object, Integer>> splitPoints = input
   .mapPartition(new SampleSplitPoint(index))
   .reduceGroup(new SplitPointReducer());

這裏的輸入row就是上文IDF的返回數值。

用allValues記錄了本task目前處理的句子有多少個單詞。

用splitPoints做了採樣。如何選擇呢,通過genSampleIndex函數。

public static Long genSampleIndex(Long splitPointIdx, Long count, Long splitPointSize) {
   splitPointIdx++;
   splitPointSize++;

   Long div = count / splitPointSize;
   Long mod = count % splitPointSize;

   return div * splitPointIdx + ((mod > splitPointIdx) ? splitPointIdx : mod) - 1;
}

後續操作也使用同樣的genSampleIndex函數來做選擇,這樣保證在操作所有序列上可以選取同樣的採樣點。

allValues = {ArrayList@10264}  size = 8  //本task有多少單詞
 0 = {Double@10266} -2.0
 1 = {Double@10271} -1.0
 2 = {Double@10272} -1.0
 3 = {Double@10273} -1.0
 4 = {Double@10274} -1.0
 5 = {Double@10275} -1.0
 6 = {Double@10276} -1.0
 7 = {Double@10277} -1.0
 
splitPoints = {ArrayList@10265}  size = 7 //採樣了7個
 0 = {Double@10266} -2.0
 1 = {Double@10271} -1.0
 2 = {Double@10272} -1.0
 3 = {Double@10273} -1.0
 4 = {Double@10274} -1.0
 5 = {Double@10275} -1.0
 6 = {Double@10276} -1.0

最後返回採樣數據,返回時候附帶當前taskIDnew Tuple2 <Object, Integer>(obj,taskId)

這裡有一個trick點

  for (Object obj : splitPoints) {
     Tuple2 <Object, Integer> cur
        = new Tuple2 <Object, Integer>(
        obj,
        taskId); //這裏返回的是類似 (-5.0,2) :其中2就是task id,-5.0是-wordcount。
     out.collect(cur);
  }

  out.collect(new Tuple2(
     getRuntimeContext().getNumberOfParallelSubtasks(),
     -taskId - 1));//這裏返回的是一個特殊元素,類似(4,-2) :其中4是本應用中并行task數目,-2是當前-taskId - 1。這個task數目後續就會用到。

具體數據參見如下:

row = {Row@10211} "中國,-1.0,1.0986122886681098"
 fields = {Object[3]@10214} 
 
cur = {Tuple2@10286} "(-5.0,2)" // 返回採樣數據,返回時候附帶當前taskID
 f0 = {Double@10285} -5.0 // -wordcount。
 f1 = {Integer@10300} 2 // 當前taskID
歸併 SplitPointReducer

歸併所有task生成的sample。然後再次sample,把sample數據組成一個數據塊,這個數據塊選擇的原則是:每個task都盡量選擇若干sample

這裏其實是有一個轉換,就是從正常單詞的抽樣 轉換到 某一類單詞的抽樣,這某一類的意思舉例是:出現次數為一,或者出現次數為五 這種單詞

這裏all是所有採樣數據,其中一個元素內容舉例 (-5.0,2) :其中2就是task id,-5.0是-wordcount。

這裏用 Collections.sort(all, new PairComparator()); 來對所有採樣數據做排序。排序基準是首先對 -wordcount,然後對task ID。

SplitPointReducer的返回採樣數值就作為廣播變量存儲起來:.withBroadcastSet(splitPoints, "splitPoints");

這裏的trick點是:

for (Tuple2 <Object, Integer> value : values) {
   if (value.f1 < 0) { 
      instanceCount = (int) value.f0;  // 特殊數據,類似(4,-2) :其中4是本應用中task數目,這個就是後續選擇哪些taskid的基準
      continue;
   }
   all.add(new Tuple2 <>(value.f0, value.f1)); // (-5.0,2) 正常數據
}

選擇sample index splitPoints.add(allValues.get(index));也使用了同樣的genSampleIndex。

計算中具體數據如下:

for (int i = 0; i < splitPointSize; ++i) {
		int index = genSampleIndex(
					Long.valueOf(i),
					Long.valueOf(count),
					Long.valueOf(splitPointSize))
					.intValue();
		spliters.add(all.get(index));
}
for (Tuple2 <Object, Integer> spliter : spliters) {
		out.collect(spliter);
}

count = 33
all = {ArrayList@10245}  size = 33 // 所有採樣數據,
0 = {Tuple2@10256} "(-5.0,2)"// 2就是task id,-5.0是-wordcount。
1 = {Tuple2@10285} "(-2.0,0)"
......
6 = {Tuple2@10239} "(-1.0,0)"
7 = {Tuple2@10240} "(-1.0,0)"
8 = {Tuple2@10241} "(-1.0,0)"
9 = {Tuple2@10242} "(-1.0,0)"
10 = {Tuple2@10243} "(-1.0,0)"
11 = {Tuple2@10244} "(-1.0,1)"
......
16 = {Tuple2@10278} "(-1.0,1)"
......
24 = {Tuple2@10279} "(-1.0,2)"
......
32 = {Tuple2@10313} "(-1.0,3)"
  
// spliters是返回結果,這裏分別選取了all中index為8,16,24這個三個record。每個task都選擇了一個元素。
spliters = {HashSet@10246}  size = 3
 0 = {Tuple2@10249} "(-1.0,0)" // task 0 被選擇。就是說,這裏從task 0中選擇了一個count是1的元素,具體選擇哪個單詞其實不重要,就是為了選擇count是1的這種即可。
 1 = {Tuple2@10250} "(-1.0,1)" // task 1 被選擇。具體同上。
 2 = {Tuple2@10251} "(-1.0,2)" // task 2 被選擇。具體同上。
SplitData把真實數據IDF插入

use binary search to partition data into sorted subsets。前面函數給出的是詞的count,但是沒有IDF。這裏將用二分法查找 找到IDF,然後把IDF插入到partition data中。

首先要注意一點:splitData的輸入就是原始輸入input, 和splitPoints的輸入是一樣 的

DataSet <Tuple2 <Integer, Row>> splitData = input
   .mapPartition(new SplitData(index))
   .withBroadcastSet(splitPoints, "splitPoints");

open函數中會取出廣播變量 splitPoints。

splitPoints = {ArrayList@10248}  size = 3
 0 = {Tuple2@10257} "(-1.0,0)"
 1 = {Tuple2@10258} "(-1.0,1)"
 2 = {Tuple2@10259} "(-1.0,2)"

本函數的輸入舉例

row = {Row@10232} "入門,-1.0,1.0986122886681098"

會在splitPoints中二分法查找,得到splits中每一個 sample 對應的真實IDF。然後發送出去。

這裏需要特殊說明下,這個二分法查找查找的是IDF數值,比如count為1的這種單詞對應的IDF數值,可能很多單詞都是count為1,所以找到一個這樣單詞的IDF即可

splitPoints = {ArrayList@10223}  size = 3
 0 = {Tuple2@10229} "(-1.0,0)"
 1 = {Tuple2@10230} "(-1.0,1)"
 2 = {Tuple2@10231} "(-1.0,2)"
curTuple.f0 = {Double@10224} -1.0
  
int bsIndex = Collections.binarySearch(splitPoints, curTuple, new PairComparator());

		int curIndex;
		if (bsIndex >= 0) {
			curIndex = bsIndex;
		} else {
			curIndex = -bsIndex - 1;
		}

// 假設單詞是 "入門",則發送的是 "入門" 這類單詞在本partition的index,和 "入門" 的單詞本身
// 其實,從調試過程看,是否發送單詞信息本身並不重要,因為接下來的那一步操作中,並沒有用到單詞本身信息
out.collect(new Tuple2 <>(curIndex, row)); 
reduceGroup計算同類型單詞數目

這裡是計算在某一partition中,某一種類單詞的數目。比如count為1的單詞,這種單詞總共有多少個

後續會把new Tuple2 <>(id, count)作為partitionCnt廣播變量存起來。

id就是這類單詞在這partition中間的index,我們暫時稱之為partition index。count就是這類單詞在本partition的數目。

// 輸入舉例
value = {Tuple2@10312} "(0,入門,-1.0,1.0986122886681098)"
 f0 = {Integer@10313} 0
 
// 計算數目
for (Tuple2 <Integer, Row> value : values) {
		id = value.f0;
		count++;
}

out.collect(new Tuple2 <>(id, count));  
  
// 輸出舉例,假如是序號為0的這類單詞,其總體數目是12。這個序號0就是這類單詞在某一partition中的序號。就是上面的 curIndex。
id = {Integer@10313} 0
count = {Long@10338} 12

4.2.2 localSort

第二步是localSort。Sort a partitioned dataset. 最終排序並且會返回最終數值,比如 (29, “主編,-1.0,1.0986122886681098″), 29就是”主編” 這個單詞在 IDF字典中的序號。

DataSet<Tuple2<Long, Row>> ordered = localSort(partitioned.f0, partitioned.f1, 1);

open函數中會獲取partitionCnt。然後計算出某一種類單詞,其在本partition之前所有partition中,這類單詞數目。

public void open(Configuration parameters) throws Exception {
		List <Tuple2 <Integer, Long>> bc = getRuntimeContext().getBroadcastVariable("partitionCnt");
		startIdx = 0L;
		int taskId = getRuntimeContext().getIndexOfThisSubtask();
		for (Tuple2 <Integer, Long> pcnt : bc) {
			if (pcnt.f0 < taskId) {
					startIdx += pcnt.f1;
			}
		}
}

bc = {ArrayList@10303}  size = 4
 0 = {Tuple2@10309} "(0,12)"  // 就是task0裏面,這種單詞有12個
 1 = {Tuple2@10310} "(2,9)"// 就是task1裏面,這種單詞有2個
 2 = {Tuple2@10311} "(1,7)"// 就是task2裏面,這種單詞有1個
 3 = {Tuple2@10312} "(3,9)"// 就是task3裏面,這種單詞有3個
// 如果本task id是4,則其startIdx為30。就是所有partition之中,它前面index所有單詞的和。  

然後進行排序。Collections.sort(valuesList, new RowComparator(field));

valuesList = {ArrayList@10405}  size = 9
 0 = {Row@10421} ":,-1.0,1.0986122886681098"
 1 = {Row@10422} "主編,-1.0,1.0986122886681098"
 2 = {Row@10423} "國內,-1.0,1.0986122886681098"
 3 = {Row@10424} "文獻,-1.0,1.0986122886681098"
 4 = {Row@10425} "李宜燮,-1.0,1.0986122886681098"
 5 = {Row@10426} "糖尿病,-1.0,1.0986122886681098"
 6 = {Row@10427} "美國,-1.0,1.0986122886681098"
 7 = {Row@10428} "謝恩,-1.0,1.0986122886681098"
 8 = {Row@10429} "象棋,-1.0,1.0986122886681098"
  
  
// 最後返回時候,就是  (29, "主編,-1.0,1.0986122886681098"),29就是“主編”這個單詞在最終字典中的序號。
// 這個序號是startIdx + cnt,startIdx是某一種類單詞,其在本partition之前所有partition中,這類單詞數目。比如在本partition之前,這類單詞有28個,則本partition中,從29開始計數。就是最終序列號
	for (Row row : valuesList) {
		out.collect(Tuple2.of(startIdx + cnt, row));
		cnt++; // 這裏就是在某一類單詞中,單調遞增,然後賦值一個字典序列而已
	}  
cnt = 1
row = {Row@10336} "主編,-1.0,1.0986122886681098"
 fields = {Object[3]@10339} 
startIdx = 28

4.3 過濾

最後還要進行過濾,如果文字個數超出了字典大小,就拋棄多餘文字。

ordered.filter(new FilterFunction<Tuple2<Long, Row>>() {
    @Override
    public boolean filter(Tuple2<Long, Row> value) {
        return value.f0 < vocabSize;
    }
})

0x05 生成模型

具體生成模型代碼如下。

DataSet<DocCountVectorizerModelData> resDocCountModel = ordered.filter(new FilterFunction<Tuple2<Long, Row>>() {
    @Override
    public boolean filter(Tuple2<Long, Row> value) {
        return value.f0 < vocabSize;
    }
}).mapPartition(new BuildDocCountModel(params)).setParallelism(1);
return resDocCountModel;

其中關鍵類是 DocCountVectorizerModelData 和 BuildDocCountModel。

5.1 DocCountVectorizerModelData

這是向量信息。

/**
 * Save the data for DocHashIDFVectorizer.
 *
 * Save a HashMap: index(MurMurHash3 value of the word), value(Inverse document frequency of the word).
 */
public class DocCountVectorizerModelData {
    public List<String> list;
    public String featureType;
    public double minTF;
}

5.2 BuildDocCountModel

最終生成的模型信息如下,這個也就是之前樣例代碼給出的輸出。

modelData = {DocCountVectorizerModelData@10411} 
 list = {ArrayList@10409}  size = 37
  0 = "{"f0":"9787310003969","f1":1.0986122886681098,"f2":19}"
  1 = "{"f0":"下冊","f1":1.0986122886681098,"f2":20}"
  2 = "{"f0":"全","f1":1.0986122886681098,"f2":21}"
  3 = "{"f0":"華齡","f1":1.0986122886681098,"f2":22}"
  4 = "{"f0":"圖解","f1":1.0986122886681098,"f2":23}"
  5 = "{"f0":"思","f1":1.0986122886681098,"f2":24}"
  6 = "{"f0":"成像","f1":1.0986122886681098,"f2":25}"
  7 = "{"f0":"舊書","f1":1.0986122886681098,"f2":26}"
  8 = "{"f0":"索引","f1":1.0986122886681098,"f2":27}"
  9 = "{"f0":":","f1":1.0986122886681098,"f2":28}"
  10 = "{"f0":"主編","f1":1.0986122886681098,"f2":29}"
  11 = "{"f0":"國內","f1":1.0986122886681098,"f2":30}"
  12 = "{"f0":"文獻","f1":1.0986122886681098,"f2":31}"
  13 = "{"f0":"李宜燮","f1":1.0986122886681098,"f2":32}"
  14 = "{"f0":"糖尿病","f1":1.0986122886681098,"f2":33}"
  15 = "{"f0":"美國","f1":1.0986122886681098,"f2":34}"
  16 = "{"f0":"謝恩","f1":1.0986122886681098,"f2":35}"
  17 = "{"f0":"象棋","f1":1.0986122886681098,"f2":36}"
  18 = "{"f0":"二手","f1":0.0,"f2":0}"
  19 = "{"f0":")","f1":0.6931471805599453,"f2":1}"
  20 = "{"f0":"/","f1":1.0986122886681098,"f2":2}"
  21 = "{"f0":"出版社","f1":0.6931471805599453,"f2":3}"
  22 = "{"f0":"(","f1":0.6931471805599453,"f2":4}"
  23 = "{"f0":"入門","f1":1.0986122886681098,"f2":5}"
  24 = "{"f0":"醫學","f1":1.0986122886681098,"f2":6}"
  25 = "{"f0":"文集","f1":1.0986122886681098,"f2":7}"
  26 = "{"f0":"正版","f1":1.0986122886681098,"f2":8}"
  27 = "{"f0":"版","f1":1.0986122886681098,"f2":9}"
  28 = "{"f0":"電磁","f1":1.0986122886681098,"f2":10}"
  29 = "{"f0":"選讀","f1":1.0986122886681098,"f2":11}"
  30 = "{"f0":"中國","f1":1.0986122886681098,"f2":12}"
  31 = "{"f0":"書","f1":1.0986122886681098,"f2":13}"
  32 = "{"f0":"十二冊","f1":1.0986122886681098,"f2":14}"
  33 = "{"f0":"南開大學","f1":1.0986122886681098,"f2":15}"
  34 = "{"f0":"文學","f1":1.0986122886681098,"f2":16}"
  35 = "{"f0":"郁達夫","f1":1.0986122886681098,"f2":17}"
  36 = "{"f0":"館藏","f1":1.0986122886681098,"f2":18}"
 featureType = "WORD_COUNT"
 minTF = 1.0

0x06 預測

預測業務邏輯是DocCountVectorizerModelMapper

首先我們可以看到 FeatureType,這個可以用來配置輸出哪種信息。比如可以輸出以下若干種:

public enum FeatureType implements Serializable {
    /**
     * IDF type, the output value is inverse document frequency.
     */
    IDF(
        (idf, termFrequency, tokenRatio) -> idf
    ),
    /**
     * WORD_COUNT type, the output value is the word count.
     */
    WORD_COUNT(
        (idf, termFrequency, tokenRatio) -> termFrequency
    ),
    /**
     * TF_IDF type, the output value is term frequency * inverse document frequency.
     */
    TF_IDF(
        (idf, termFrequency, tokenRatio) -> idf * termFrequency * tokenRatio
    ),
    /**
     * BINARY type, the output value is 1.0.
     */
    BINARY(
        (idf, termFrequency, tokenRatio) -> 1.0
    ),
    /**
     * TF type, the output value is term frequency.
     */
    TF(
        (idf, termFrequency, tokenRatio) -> termFrequency * tokenRatio
    );
}

其次,在open函數中,會加載模型,比如:

wordIdWeight = {HashMap@10838}  size = 37
 "醫學" -> {Tuple2@10954} "(6,1.0986122886681098)"
 "選讀" -> {Tuple2@10956} "(11,1.0986122886681098)"
 "十二冊" -> {Tuple2@10958} "(14,1.0986122886681098)"
...
 "華齡" -> {Tuple2@11022} "(22,1.0986122886681098)"
 "索引" -> {Tuple2@11024} "(27,1.0986122886681098)"
featureType = {DocCountVectorizerModelMapper$FeatureType@10834} "WORD_COUNT"

最後,預測時候調用predictSparseVector函數,會針對輸入 二手 舊書 : 醫學 電磁 成像來進行匹配。生成稀疏向量SparseVector。

0|$37$0:1.0 6:1.0 10:1.0 25:1.0 26:1.0 28:1.0

以上表示那幾個單詞 分別對應0 6 10 25 26 28 這幾個字典中對應序號的單詞,其在本句對應的出現數目都是一個。

0x07 參考

Tf-Idf詳解及應用

https://github.com/fxsjy/jieba

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

kubernetes pod內抓包,telnet檢查網絡連接的幾種方式

背景

在日常kubernetes的運維中,經常遇到pod的網絡問題,如pod間網絡不通,或者端口不通,更複雜的,需要在容器裏面抓包分析才能定位。而kubertnets的場景,pod使用的鏡像一般都是盡量精簡,很多都是基於alpine基礎鏡像製作的,因而pod內沒有ping,telnet,nc,curl命令,更別說tcpdump這種複雜的工具了。除了在容器或者鏡像內直接安裝這些工具這種最原始的法子,我們探討下其他法子。

實現

kubectl debug插件方式

項目地址 kubect debug,https://github.com/aylei/kubectl-debug

kubectl-debug 是一個簡單的 kubectl 插件,能夠幫助你便捷地進行 Kubernetes 上的 Pod 排障診斷。背後做的事情很簡單: 在運行中的 Pod 上額外起一個新容器,並將新容器加入到目標容器的 pid, network, user 以及 ipc namespace 中,這時我們就可以在新容器中直接用 netstat, tcpdump 這些熟悉的工具來解決問題了, 而舊容器可以保持最小化,不需要預裝任何額外的排障工具。操作流程可以參見官方項目地址文檔。

一條 kubectl debug命令背後是這樣的

步驟分別是:

  1. 插件查詢 ApiServer:demo-pod 是否存在,所在節點是什麼
  2. ApiServer 返回 demo-pod 所在所在節點
  3. 插件請求在目標節點上創建 Debug Agent Pod
  4. Kubelet 創建 Debug Agent Pod
  5. 插件發現 Debug Agent 已經 Ready,發起 debug 請求(長連接)
  6. Debug Agent 收到 debug 請求,創建 Debug 容器並加入目標容器的各個 Namespace 中,創建完成后,與 Debug 容器的 tty 建立連接

接下來,客戶端就可以開始通過 5,6 這兩個連接開始 debug 操作。操作結束后,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成。

直接進入容器net ns方式

有2種進入pod 所在net ns的方式,前提都是需要登錄到pod所在宿主機,且需要找出pod對應的容器ID或者名字。

ip netns方式

  • 獲取pod對應容器的ID或者name

    pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)" #替換為環境實際的容器名字或者uuid
    
  • 創建容器對應netns

    ip netns會到/var/run/netns目錄下尋找network namespace,把容器進程中netns連接到這個目錄中后,ip netns才會感知到

    $ sudo mkdir -p /var/run/netns
    
    #docker默認不會創建這個鏈接,需要手動創建,這時候執行ip netns,就應當看到鏈接過來的network namespace
    $ sudo ln -sf /proc/$pid/ns/net "/var/run/netns/<container_name|uuid>" 
    
  • 執行ip netns <<container_name|uuid > bash,進入容器ns

    ip netns exec <container_name|uuid>  bash
    
  • 執行telnet,tcpdump等命令,此時執行ip a或者ifconfig,只能看到容器本身的IP

如下圖,執行ifconfig,只看到容器本身的IP,此時執行telnet,tcpdump等於直接在容器內操作

nsenter方式

nsenter為util-linux裏面的一個工具,除了進入容器net ns,還支持其他很多操作,可以查看官方文檔。

pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)"
nsenter -t $pid -n /bin/bash
tcpdump -i eth0 -nn  #此時利用宿主機的tcpdump執行抓包操作,等於在容器內抓包

總結

  1. kubectl debug方式功能更強大,缺點是需要附加鏡像,要在目標pod創建debug agent的容器,比較笨重,但是優點是能使用的工具更多,不需要ssh到pod所在節點,除了netstat,tcpdump工具,還能使用htop,iostat等其他高級工具,不僅能對網絡進行debug,還能對IO等其他場景進行診斷,適用更複雜的debug場景。
  2. 直接進入容器net ns方式相對比較輕量,復用pod所在宿主機工具,但魚和熊掌不可兼得,缺點是只能進行網絡方面的debug,且需要ssh登錄到pod所在節點操作。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

2.0T+7速雙離合 神車哈弗H6又出新款競爭力超強?

藍標版的進氣格柵較小,看起來更加精緻,紅標版本將採用大尺寸的進氣格柵(這是紅標版的一貫造型)看起來運動感十足,非常霸氣,底部霧燈區域的造型的尺寸也會更大,可以說紅標版和藍標版的不同點主要集中在前臉部分。

如今SUV大賣熱賣,受到消費者的熱捧,但是說起國內的SUV,那就不得不提哈弗了,因為就銷量來看,哈弗是當之無愧的自主SUV大哥。

作為哈弗最成功的一個車型,H6給哈弗貢獻了太多的銷量,今年10月份H6賣了56667輛,1-10月份累計賣了429896輛,甩第二名好幾條街。

但是這個銷量是有貓膩的,細心的讀者可以留意到每次看到銷量榜的時候,無論怎麼樣仔細查找,都找不到H6 coupe這款車。其實答案是這樣的。H6 coupe和H6完全不是同一台車,是全新的一代車型,主打運動風,但是在統計銷量的時候,H6 coupe是算到H6車型裏面的。根據長城官方的透漏,H6 coupe的月銷量佔H6銷量的30%左右。

所以,你看到H6的銷量才會那麼高。同時,H6 coupe的市場容量也挺大的。目前在售的H6 coupe搭載1.5T和2.0T發動機,為藍標版車型,那麼對於喜歡拉皮換殼折騰的哈弗來說,這遠遠是不夠的。所以就像H2s推出紅標版和藍標版一樣。H6 coupe也打算這樣折騰,推出紅標版車型。

廣州車展,H6 Coupe紅標版車型已經亮相了,同時我們在國家工信部官網看到了一款哈弗全新SUV的申報信息,所以我們大膽的猜測,這個車子就是即將上市的H6 COUpE紅標版車型。

長城的用意很明確,就是造出盡可能多的車型,滿足不同的消費者對外觀的不同需求,爭取銷量最大化,H6 Coupe紅標版和在售的藍標版主要的變化體現在外觀和全新的2.0T發動機。

藍標版的進氣格柵較小,看起來更加精緻,紅標版本將採用大尺寸的進氣格柵(這是紅標版的一貫造型)看起來運動感十足,非常霸氣,底部霧燈區域的造型的尺寸也會更大,可以說紅標版和藍標版的不同點主要集中在前臉部分。

但是到了側面和尾部,差別就比較小了,畢竟還是“臉”最重要麼。不過,H6 coupe的懸浮式車頂的造型看上去倒是很時尚,賣點不少。

內飾造型和在售藍標版完全一致,看起來簡潔大方,內飾用料很實在,做工很精細,看起來很有檔次感。

其實說到這裏大家也都知道了,紅標版和藍標版的差別並不大,只是外觀有少許差別。另外新車將會搭載GW4G15B 1.5T和GW4C20 2.0T汽油發動機,匹配6速手動、7速雙離合變速箱。

紅標版車型的長寬高為4590/1845/1700mm,軸距為2720mm。同時我們根據申報的信息來看,1.5T車型的百公里綜合油耗申報值為7.4L/100km,2.0T車型的為8.5L/100km。當然實際油耗肯定不止這麼低。

總結:和H2s的紅藍標車型一樣,H6 coupe也是搞兩個外觀有差別,但是內在都一樣的車子,所以對於這種車型,你只需要考慮喜歡哪個外觀就行。不過紅標版的H6 coupe上市以後,也會面諸如博越、榮威RX5等這些強大的對手。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

廣州車展 這些“見所未見”的轎跑/SUV絕不能錯過

渾身肌肉一臉野性的“美式壯man”近一個世紀以來,雪佛蘭皮卡不斷演化——讓人們體驗到更加豐富而時尚的生活樂趣。本次索羅德Silverado、庫羅德Colorado的展示,也讓中國的消費者真正的認識以及觸碰到皮卡,其像無畏的騎士,是一種血性的代表,彷彿每時每刻都想衝出家門去越野。

雪佛蘭誕生於賽道之上,但卻回歸到群眾之中。雪佛蘭給我們的印象是一個普及化的品牌,產品覆蓋面很廣,服務於各種人群,但有很多人不知道的是,第一輛雪佛蘭汽車其實是一輛高性能的高端車型,Classic six不僅在性能上超越當時市面上的車型,搭載的發動機在技術上也是領先同行,雖然售價偏高,但是一炮打響了雪佛蘭的金字招牌,就如創始人路易·雪佛蘭的賽車手熱誠一般,在人們心中留下深深的激情勇敢、創新突破的印跡。

縱然雪佛蘭在那之後調整了方向,更加側重於製造服務廣大受眾的汽車,但這樣的DNA已深入雪佛蘭汽車的骨髓,雪佛蘭的產品一直是高品質駕駛樂趣的代表,歷經百年後,如今的雪佛蘭已經是一種生活態度的體現。在本次的廣州車展上,這個百年金領結更是給大家展示了一道“見所未見”的風景線,年輕的轎跑,硬漢的皮卡,極致的跑車,你想要的這裏都有。

從變形金剛穿越的燃爆大黃蜂

沒錯,如今展示在廣州車展舞台的就是《變形金剛》電影里鼎鼎大名的“大黃蜂”—— 全新第六代科邁羅。雖身處展台,但從前臉的進氣格柵,經典肌肉線條,LED扁平前大燈,都能讓人感受到科邁羅馭風疾馳的賽道氣息。

自誕生以來,這輛有着“非凡魅力“的大黃蜂,其轟鳴便橫掃了國際各大賽事。這也得益於創始人路易·雪佛蘭,在雪佛蘭公司成立之前,他就是一名賽車手,每天夢想的就是生產高品質的跑車,直至今天的通用汽車總裁丹·阿曼先生都是賽車的好手。很顯然,這個金領結早在誕生時,就與運動一直捆綁着。

硬派氣質秒殺同級的探界者Equinox

身為SUV陣營“主導者”——探界者Equinox,在碰撞展檯燈光之間,顯露了十足的美式力量。同時也在用一種無聲的力量詮釋着探界者的英文名Equinox所表達的含義:晝夜均分的春分與秋分時節,蓄含突破與改變現狀的力量,不受界限與束縛地探索各種可能。

另外,更值得一提的是,在小排量渦輪發動機成為主流的當下,誠然各大廠商的技術已不存在明顯的“代差”,但毋庸置疑通用的發動機一直處於一線。目前在Equinox車上配備了9AT變速箱,從机械結構上來看,沒有採用絞牙式的離合器,但體積沒有增大,平順性會有更好的保證。

渾身肌肉一臉野性的“美式壯man”

近一個世紀以來,雪佛蘭皮卡不斷演化——讓人們體驗到更加豐富而時尚的生活樂趣。本次索羅德Silverado、庫羅德Colorado的展示,也讓中國的消費者真正的認識以及觸碰到皮卡,其像無畏的騎士,是一種血性的代表,彷彿每時每刻都想衝出家門去越野。

這兩款皮卡,跟我們通常認知的皮卡可以說不是一種產物,雪佛蘭的皮卡絕對稱得上高級好開。它們的加入,也將會讓許久沒有全新車型加入的中國高端皮卡市場競爭更加激烈,很是值得期待。

索羅德Silverado

庫羅德Colorado

此次車展,雪佛蘭帶給消費者的不單是一場視覺上的盛宴,更是一個關於夢的兌現。時光荏苒,這個金領結已經走過百年,從K系列與福特T型車比肩開始,成功度過大蕭條,不斷推出新產品,甚至還一定程度上為現在汽車製造工業奠定了基礎。

雪佛蘭已橫跨140多個國家與地區,馳騁了無數世界超級賽事,征服了全球2.3億車主,同時,在品牌建設上的突破,也取得耀眼的成績——作為上海迪斯尼官方度假區的官方汽車合作品牌,獨家贊助了極具標誌性景點的“創極速光輪”,還與曼聯聯合為廣大球迷帶來眾多精彩賽事,贊助紅粉筆計劃為鄉村支教事業出一份力。

(快閃視頻)

說了這麼多,“棒球、熱狗、蘋果派,不能少了雪佛蘭”。雪佛蘭已經完成了美國夢,也終將成為與可口可樂、蘋果一樣的icon,從美國夢成就世界夢。你我的生活也可能都逃不過一輛雪佛蘭。而本次車展無論你聚焦於以上任何一款產品,它都能代表雪佛蘭。

未來雪佛蘭還將會繼續研發與進步,夢想不止,夢創未來。無論是新能源,車聯網,自動駕駛等等有關未來出行的一切,雪佛蘭都有着充足的技術儲備,期待雪佛蘭將持續以一種高品質的姿態,帶給你我更多“見所未見“的產品。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

排量小動力猛!10.9萬起緊湊型家用車更貴了反而更值得買?

2T發動機最大馬力116匹,峰值扭矩185牛米,與之匹配的是一台模擬八個檔位的S-CVT無級變速箱,由於定位依舊是一款家用轎車,在轉向手感建立和油門響應的調校方式上還是保留了雷凌以往較為輕盈的特性,駕駛依舊平易近人。除了發動機,它貴在哪。

雷凌1.2T

自從卡羅拉推出了1.2T版本以後,作為與其同平台的雷凌也很快的裝配上了同樣的渦輪增壓發動機,由於雷凌的設計會顯得更加的運動激進化一些,這也是很多年輕人的首次購車選擇,但是在很多人眼裡,1.2T的雷凌上市以後比以往要貴了幾千塊,這車到底還值得購買嗎?

廣汽豐田雷凌1.2T

指導價格:10.98-13.38萬

外觀並無變化

廣汽豐田雷凌的新款1.2T渦輪增壓車型畢竟只是一款中期改款車型,在外觀上並沒有什麼過多的變化,僅僅是在頂配車型當中增加了一套LED光源的日間行車燈以及中網採用了鋼琴黑騎的裝飾,值得一提的是,廣汽豐田雷凌1.2T新增了一種名為琥珀棕的車身配色。

運動化的內飾是為亮點

內飾設計其實也沒多少改動,只是在原有的基礎上增添了紅色的裝飾,添置於中控台和門板上,增加了內飾視覺感官上的精緻度,並且將前排座椅改動成為帶兩側護翼的運動型座椅設計,縫線工藝所綉出來的LEVIN字樣彰顯着身份,也讓雷凌內飾看上去更加動感。

小排量渦輪增壓才是重點

我們可以注意到1.2T雷凌的尾標上標註的是D-4T的標識,意為:Dirct-injuction 4 stroke gasoline engine with Turbo.翻譯過來就是:四衝程缸內直噴渦輪增壓發動機。與市面上多數“少了排量就少了缸數”的小排量三缸渦輪增壓機不同,豐田這款1.2T發動機依然使用了直列四缸的布局,更多的缸數也意味着這款發動機在運轉過程中可以保持優良的平順性。

由於使用了豐田雙VVT-iW可變氣門正時技術、以及使用缸內直噴的噴油方式,這款發動機的升功率也會相應增高,而且這款發動機的內燃機熱力循環方式可以在奧拓循環和阿特金森循環之間切換,所以在保證了發動機工作效率的同時,也保證了燃油經濟性。

雷凌1.2T發動機最大馬力116匹,峰值扭矩185牛米,與之匹配的是一台模擬八個檔位的S-CVT無級變速箱,由於定位依舊是一款家用轎車,在轉向手感建立和油門響應的調校方式上還是保留了雷凌以往較為輕盈的特性,駕駛依舊平易近人。

除了發動機,它貴在哪?

很多人會覺得,既然換了渦輪增壓發動機肯定在技術成本上就變得更加昂貴了,但其實我們可以對比一下,作為以往雷凌的主力車型1.6L自然吸氣版本指導價格為10.78-13.08萬,而雷凌1.2T的指導價格為10.98-13.38萬,但雷凌1.2T的配置卻是對得起它的售價。

雷凌1.2T車型標配了發動機啟停系統,並且增加了車身穩定系統,牽引力控制系統,以及上坡輔助系統,這些主動安全配置的搭載在以往的1.6L車型中並沒有裝配,如此看來這兩三千的定價換來的是更多的安全配置,這波交易並不虧。

全文總結:與雙胞胎車型卡羅拉不同的是,雷凌1.2T的上市是作為全面取代1.6L自然吸氣版本的角色存在,所以從主動安全配置上看雷凌的性價比有所提高,而1.2T渦輪增壓的搭載動力表現也比原來作為主力的1.6L車型更好,所以從品控、定位以及車型本身的綜合產品力來說,雷凌仍舊是一款不錯的值得購買的合資緊湊型家用轎車。至於究竟是否值得購買,那就要看你對於日系車,對於豐田品牌的接受程度有多高了,本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

這台800萬的自主車 馬化騰 劉強東 雷軍人手一台

全球最快電動車發布 蔚來 Ep9動力方面搭載了4台高性能電機以及4個獨立變速箱,最終能夠輸出1,360匹馬力,0到200公里加速7。1秒,極速313公里。Ep9採用彈匣式可換電池系統,快充模式下充滿電僅需45分鐘,續航里程可達427公里。搭載的DRS可調擾流控制系統,包括三種可調模式的動態尾翼系統,和全尺寸底盤擴散器等空氣動力裝置,使得Ep9在每小時240公里的速度下能夠獲得高達24,000牛的下壓力。

思域1.0T正式發布

本次只有手動/自動各一個車型,價格分別是11.59萬和12.79萬元。相比1.5T的豪華型,前排側氣囊、前後排頭部氣簾、無鑰匙進入/啟動、電動天窗、后駐車雷達、全液晶儀錶盤、後排杯架、後排中央扶手、中控屏、前霧燈、后視鏡加熱、自動空調統統都沒有了,3缸發動機還簡配成這樣,大致意思就是說這已經是最低價了,所以 1.5T的車型該加價的加價,該等車的等車繼續等吧。

樂視 lucid motors

官方表明,從造電池到造車,Atieva算是進行了一個大的跨越,而其核心競爭力則是其獨特的電池冷卻和能量管理技術,電池的能量密度比其他競爭對手普遍高出20%,續航里程可以輕鬆超過480km,它的最大馬力可以達到1200匹,最後為了安全起見被設置在900匹。儘管如此,它的百公里加速時間也可以達到2.69秒,還沒看到實車在路上跑之前,我們就看看咯。

全球最快電動車發布 蔚來 Ep9

動力方面搭載了4台高性能電機以及4個獨立變速箱,最終能夠輸出1,360匹馬力,0到200公里加速7.1秒,極速313公里。Ep9採用彈匣式可換電池系統,快充模式下充滿電僅需45分鐘,續航里程可達427公里。

搭載的DRS可調擾流控制系統,包括三種可調模式的動態尾翼系統,和全尺寸底盤擴散器等空氣動力裝置,使得Ep9在每小時240公里的速度下能夠獲得高達24,000牛的下壓力。

紀錄片显示Ep9在10月12日德國紐博格林北環賽道進行的測試中,創造了7分05秒的最快電動汽車圈速,公司創始投資人包括雷軍,馬化騰,奶茶妹夫劉強東,整個發布會過程中透露Ep9的造價達到了120萬美元,據之前消息稱未來量產的首款車型將會是一款20萬左右的SUV車型,大家覺得怎麼樣?

新能源汽車騙補貼披露 金龍汽車罰款近8億

本月財政部向工信部抄送了《財政部行政處罰事項告知書》,確認蘇州金龍公司申報2015年度中央財政補貼資金的新能源汽車中,有1683輛車截至2015年底仍未完工,但在2015年卻提前辦理了機動車行駛證。涉及補助資金5.19億元。根據相關法規,工信部將責令蘇州金龍公司停止生產和銷售問題車型,暫停蘇州金龍公司申報新能源汽車推廣應用推薦車型資質,並將問題車型從《新能源汽車推廣應用推薦車型目錄》予以剔除,進行為期6個月整改,整改完成后,工信部將對整改情況進行驗收。

最終追回罰款達7.78億,其實不止金龍汽車,現在國內93家新能源汽車企業中有72家存在有騙補貼的行為,這一次的強力打壓會不會對以後的新能源電動車發展有所改進呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

這台美系三廂車賣得好 為啥還堅持推出兩廂車型

4T 雙離合先鋒天窗版指導價:13。99萬天窗版和炫鋒版(14。99萬)只相差1萬元,但在配置只是多了真皮方向盤、倒車視頻影像、真皮座椅、後排杯架、在差價上並不至一萬,而且天窗版的性價比更高。領鋒版的售價是16。99萬,在配置上可謂是一應俱全,但編者認為這個價格,全車落地都接近20萬,還不如買一台B級車,空間更大。

科魯茲也更新換代了,全新的車型也受到消費者的追捧,而且和上一代車型相比,也更加省油了,底盤的調校也偏舒適。至於剛上市的科魯茲就有不錯的銷量,10份就賣出了11191台,這也源自於上代車型給消費者帶來不錯的口碑。

全新科魯茲和上一代車型相比,感覺是完成了瘦身,凌厲的車身線條,更顯動感,前臉的設計也是沿用了雪佛蘭家族化的設計,就像是一頭溫柔的猛獸。整體視覺感官科魯茲更顯年輕時尚。

科魯茲在內飾採用“環抱式”的設計,有種被包裹着的安全感,中控設計雖然簡潔卻不簡單,空調面板加了大量鋼琴漆的點綴,在做工用料上面,中控台採用了皮質縫線的設計,營造出上檔次的氛圍,全黑的內飾更加年輕化。

上一代科魯茲,空間也一直是它的短板。全新科魯茲也改進了不少,在空間上並不會像上代車型這麼緊湊,對於應付日常出行毫無壓力。

科魯茲的售價為: 8.99-16.99萬。科魯茲共搭載4款發動機總成,分別是1.5L、1.6L自然吸氣發動機、1.4T、1.6T渦輪增壓發動機,傳動系統配備的是6擋手動、6擋手自一體、7擋雙離合,在動力的輸出表現上,自吸組合更平順、而渦輪組合會激進一些,只要是日常代步,動力可是綽綽有餘。在燃油經濟性上面,也比上代車型更加的省油。底盤的調校也更為舒適,更為居家了。那麼我們下面說說,那款車型更適合您。

1.4T推薦車型:

科魯茲 2017款 1.4T 雙離合先鋒天窗版

指導價:13.99萬

天窗版和炫鋒版(14.99萬)只相差1萬元,但在配置只是多了真皮方向盤、倒車視頻影像、真皮座椅、後排杯架、在差價上並不至一萬,而且天窗版的性價比更高。領鋒版的售價是16.99萬,在配置上可謂是一應俱全,但編者認為這個價格,全車落地都接近20萬,還不如買一台B級車,空間更大。具體見下錶:

1.5L推薦車型:

科魯茲 2017款 1.5L 手動先鋒版

指導價:10.99萬

購買手動車型,我更推薦乞丐版,因為手動先鋒版和手動炫鋒版(12.49萬)價格差價上相差了一萬五千,只是多了個幾個不實用的配置,畢竟是作為家用代步車,在配置主要以實用為主,而且性價比高!具體見下錶:

至於1.6L和1.6T兩款車型都是掀背版,想入手的朋友,編者認為暫且可以等一等,在廣州車展上,全新的科魯茲掀背版已亮相,但在價格上還未公布。

在外觀的設計上更具時尚年輕感,流暢的車身線條更加飄逸。在發動機總成上,傳承了三廂版的動力,1.4T渦輪增壓發動機,最大功率為150千瓦,最大扭矩240牛米,傳動系統也是匹配的7擋雙離合。1.5L自然吸氣發動機最大扭矩146牛米,最大功率114千瓦,傳動系統匹配的是6擋手動和6擋自動變速箱。

編者點評:

全新科魯茲可謂是全新換代,變得更居家更舒適,而且也擁有同級別中相對優秀的經濟油耗,在變速箱的調校上也不會像上代車型有嚴重的頓挫感。時尚動感、年輕,科魯茲還是非常值得購買!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

M1 Mac 不僅有黑科技的高效率,也讓安全真正「到位」

體驗過了搭載 Apple Silicon 晶片的新世代蘋果桌上電腦,的確讓人驚艷於其在效率堪稱黑科技的表現。然而我們沒有注意到,甚至在先前 M1 Mac 發表會也僅以「搭載新世代 Secure Enclave 安全隔離區」淡淡帶過的部分,其實深藏不露的是蘋果歷時十多年對於安全方面的努力。最終也讓 Mac 用戶得以在享受高效能體驗的同時,得以保有包括生物辨識、密碼與資料的最高「安全感」。繼續閱讀 M1 Mac 不僅有黑科技的高效率,也讓安全真正「到位」報導內文。 

▲圖片來源:Apple

M1 Mac 不僅有黑科技的高效率,也讓安全真正「到位」

農曆春節期間,Apple 釋出了新版滿滿 200 多頁的 Apple 平台安全性的完整指南。裡面在硬體與系統安全性都有提供全盤的說明,更針對資料加密、服務安全性乃至於行動世代開始令人無法忽視的 App 應用服務方面,提供了整個蘋果生態系的安全相關資訊。

從初代 iPhone 為企業用戶製作僅有「 1 頁」的說明啟程。這 10 多年來,蘋果為了行動世代乃至於到 M1 晶片的後電腦時代,的確都因為自主打造晶片為基礎所提供的優勢,而能從晶片出發紮紮實實打造生態系,以用戶所需的資訊安全角度出發來打造軟硬體功能,更因此獲得許多資安專家認可為最全面防護裝置,可說是蘋果生態系的隱形冠軍。

對應貼身行動裝置在安全上的更嚴苛要求(畢竟真的越來越多敏感資訊存在裡面了呢)。從 iPhone 開始,蘋果其實就已經利用自有晶片的優勢為用戶提供更安全的行動裝置使用體驗。有意思的是,即便一開始 iPhone 就提供了先進的檔案保護系統。但面對每人每天平均 80 次解鎖的需求,其實還是等到了 iPhone 5s 開始 Touch ID 指紋辨識加入後,才普遍讓人願意為手機加上這道防線,讓蘋果行動裝置資訊安全努力能更被善用。

畢竟無論家中的門有多強多安全,如果開鎖的方法麻煩到你都懶得鎖上也是白搭(笑)。至於隔離區的概念,大致上就像金庫了。它可以存放產生加密金鑰、保護資料與生物特徵識資訊等項目。提供相對於一般檔案系統更進階的防護。

確保急迫需要完整安全防護的行動裝置後,為了讓 Mac 也享有安全隔離區的保護,蘋果後續也在為電腦裝置裝上 Touch ID 的同時,導入可以確保系統開機、Apple Pay、FileVault 檔案加密及在本地端存進指紋以確保不會被盜用的 T 系列安全晶片 — 其實非 Touch ID 機種也有。

▲圖:在 M1 SoC 之前,Mac 需透過 T 系列晶片來滿足安全方面的需要。(來源:Apple)

現在,透過搭載 M1 晶片的 Mac 電腦,蘋果也終究讓所有源自 iOS / iPadOS 的完整安全功能,能夠在 Apple Silicon 電腦運行並完整保護 macOS 系統的使用者。以先前 T2 晶片所能提供的保護項目為基礎,再新增了包括逐檔案的資料保護、系統完整性與密碼防護機能。更讓機器學習的機能能夠在保護資料隱私與安全的狀態下於 M1 Mac 運行。

透過自有晶片的努力,Mac 終於在去年正式讓生態系統的安全性完全到位。不過為了對應持續不斷的環境變化,也能發現 Apple 還做了不少努力。包括時常提供能讓用戶願意盡快安裝的 OTA 更新。此外,他們其實還有提供優渥獎金的 Apple Security Bounty Program 賞金計畫。最近更在 iMessage 訊息服務導入讓駭客「越來越難」入侵,對流量進行隔離來將攻擊時間越拉越長的 BlastDoor 防護機制,也算是考量安全性與隱私的功能技術。

講起來,在資安方面的範疇,蘋果之所以能獲得不錯的評價。的確在軟硬體與服務方面的高度整合有很大的幫助。但最關鍵之處,應該還是願意與多方合作並不斷用新的視角審視安全原則的態度吧。真的要以快速反應對應,才能在這個瞬息萬變的時代永遠站在第一線為用戶提供充滿安全感的使用體驗。

延伸閱讀:

讓學習贏在起跑點,來看學霸分享 iPad 搭 Apple Pencil 的高效率讀書筆記法

Pixel「車禍偵測」助翻車意外昏迷者報警脫困!雖然他開的是山貓起重機(咦)

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

Redmi AirDots 3 真無線耳機全新繽紛配色驚喜登場:30小時超長續航、藍牙 5.2 、升級圈鐵雙單元提升音質表現

近年真無線藍牙耳機已經逐漸成為民眾聆聽音樂的選擇,在 Redmi K40 系列旗艦新機發表會除了 RedmiBook Pro 筆電和 Redmi MAX 86″ 超大螢幕電視,也推出全新一代的 Redmi AirDots 3 真無線藍牙耳機。外觀方面, Redmi AirDots 3 配色更加繽紛,擁有 30 小時最長續航、系列首次採用圈鐵雙單元大幅提升音質表現,藍牙 5.2 改善畫面不同步、卡頓等干擾問題讓連線更穩定。

Redmi AirDots 3 真無線耳機全新繽紛配色驚喜登場:30小時超長續航、藍牙 5.2 、升級圈鐵雙單元提升音質表現

過去幾代 Redmi AirDots 系列真無線藍牙耳機(台灣命名為小米 EarBuds 超值版),以平易近人的價格、超高的 CP 值,成為許多預算考量消費者在真無線耳機的首選之一。在 Redmi K40 系列新機發表會的最後,也悄悄推出了全新一代的新品 Redmi AirDots 3  。

外觀方面, Redmi AirDots 3 推出藍色、粉色和白色三款全新繽紛配色:

Redmi AirDots 3 也是 AirDots 系列首次搭載圈鐵雙單元(高頻動鐵+低拼動圈)的耳機,兼具低音聲場及低音細節,高中低頻都能還原均衡且飽滿的音色:

搭載全新 Qualcomm 3040 晶片,同時升級為藍牙 5.2 技術,有效改善過去影音不同步、卡頓干擾等問題,讓聲音傳輸更快、更穩。另外, Redmi AirDots 3 也支持 aptX Adaptive 音頻解碼技術,兼具高解析度音質與低延遲的優勢,無論追劇聽歌、娛樂遊戲都帶來更流暢的體驗:

續航方面, Redmi AirDots 3 單只耳機充滿電可使用約 7 小時,搭配內建 600mAh 充電盒最長續航可達 30 小時:

Redmi AirDots 3 支持智慧感應檢測,採用內建紅外線光學感應器可即時幾冊耳機佩戴狀態,入耳瞬間即可感應,摘下耳機自動暫停播放。

Redmi AirDots 3 在體驗上也有明顯升級,此次將過去實體按鍵操控改為觸控操作,解決耳機外觀按鈕設計的突兀、減緩案件壓迫感。使用者可透過輕觸右側耳機兩下暫停/播放音樂、輕觸左耳兩下喚醒語音助理,輕觸多功能鍵接聽/掛斷電話:

Redmi AirDots 3 支持語音助理,可透過輕觸耳機喚醒:

搭配支持 MIUI 螢幕彈窗連接功能的智慧型手機,只要打開耳機盒蓋在手機端螢幕即可彈出連接畫面進行連接,同時也會在手機畫面顯示耳機、充電盒的電量資訊:

其他機身細節部分, Redmi AirDots 3 採用 USB Type-C 充電接口,充電盒具備一鍵連接按鈕:

在 Redmi AirDots 3 充電盒外配備電量指示燈,用戶能根據亮燈情況瞭解充電盒剩餘電量,耳機本體也支持 IPX4 等級生活防潑水:

售價方面, Redmi AirDots 3 真無線藍牙耳機建議售價為人民幣 199 元(約合新台幣 860 元)。

圖片/消息來源:小米官網(中國)

延伸閱讀:
Redmi K40 系列正式發表: K40/K40 Pro/K40 Pro+ 三旗艦全系列搭載高通 8 系列旗艦處理器,售價約 8,645 元起

Redmi MAX 86 超大螢幕電視發表:86 吋超大螢幕 4KHDR ,售價只要約 34,596 元

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

微軟除了讓 Remix 3D 網站被消失,還準備在 21H2 版系統中移除「3D 物件」資料夾

2017 年 4 月,微軟推出了 Windows 10 的新版本創作者更新。在這一版更新當中,微軟為這個新世代系統帶來了一些 3D 創作相關應用,像是 Remix 3D 網站與 Paint 3D (小畫家 3D)軟體等。為了幫助用戶管理這些 3D 創作作品,還特地在檔案總管的側欄加入「3D 物件」資料夾。一年後,微軟發現這些 3D 創作軟體或服務似乎相當不受歡迎,直接關了 Remix 3D 網站。最近更是直接對 Windows 10 上的 3D 物件資料夾動刀。讓這個不太受歡迎的快捷資料夾隱藏起來:

▲(圖片來源)

小畫家 3D 雖然還是跟傳統小畫家軟體那樣好用,甚至坊間有不少關於小畫家 3D 的教學功能介紹,不過其他的 3D 軟體或服務可沒那麼幸運。很多不怎麼接觸 3D 創作功能的人對於檔案管理介面中的 3D 物件資料夾相當感冒。筆者在網路上也看到國內外有許多人提供了修改機碼來隱藏這個資料夾的教學。但修改機碼終究還是有些風險,為了一個資料夾,未必要這麼大動干戈。

功能資料夾既要刪,又不能真刪

微軟在測試新系統時顯然也知道網路上的這股風氣。畢竟玩 3D 設計、3D 列印之類的創作者雖然不少,但不是每個 Windows 10 用戶都需要這些功能。於是微軟開始把「3D 物件」這個資料夾的移除,或者說隱藏,做為下一版系統的一項變更項目。不過「小畫家 3D」這個軟體仍然會預裝在下一版系統中。如果玩家連小畫家 3D 都覺得不需要,可以透過 WIndows Store 來移除:

▲關於移除 3D 物件資料夾的部分,國外就有高手製作好移除或還原用的機碼註冊檔(圖片來源)

目前的 Windows 10 Insider Preview Build 21322 就已經將 3D 物件資料夾從檔案總管的側欄移除,這意味著打開檔案總管時,不會再看到這個資料夾卡著一個位子在介面上。不過,這不代表 3D 物件資料夾徹底人間蒸發,你還是可以透過 C:\Users\用戶名稱\3D Object 這個路徑找到這個資料夾。讓有需要的人自己釘選在明顯的地方,例如工作列、桌面或檔案總管的側欄:

▲微軟要讓它消失,卻又不能真正消失,只好用這種變戲法的形式讓它退居幕後(圖片來源)

換句話說,微軟的解決方案有些消極,變個戲法讓資料夾不在表面出現,卻還是能在深層的地方發現他的存在。相信微軟還是多少要顧及仍有需要利用 3D 物件資料夾的用戶意見。這點算是折衷的保留了雙方的需求,所能呈現的最好成果。如果微軟 Windows 10 21H2 版本沒有延遲太久的話,預計年底的時候就會正式亮相了。

消息來源

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價