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維修中心

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

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

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

廣州車展 這些“見所未見”的轎跑/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維修中心

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

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

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

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

從去年底小米11 發表後,今年一月初小米的「友商」紅米 Redmi 也開始預告新一代 Redmi K40 系列即將推出的消息。當時在發表會前直接提前預先公佈售價為人民幣 2,999 元已經引起許多討論,不過在今(25)日晚間的 Redmi K40 系列發表會則帶來兩款高通 8 系列旗艦處理器共三款旗艦新機,性價比再次帶來更多驚喜。

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

在今日發表會之前, Redmi 官方就已經預告這次「K40 系列」雙旗艦發表會將帶來兩款旗艦級處理器的新機,不過最終一次發表了共三款旗艦機,分別為搭載高通 Snapdragon 870 處理器的 Redmi K40 以及搭載高通 Snapdragon 888 處理器的 Redmi K40 Pro 、 Redmi K40 Pro+ 。

Redmi K40

首先看到目前 Redmi K40 系列三款機型中最「入門」的 Redmi K40 標準版,以往 Redmi K 系列手機在標準版其實都算是中高階的定位,不過這次在 Redmi K40 仍然是定位為真旗艦的手機。

硬體規格方面, Redmi K40 為首批搭載 Qualcomm Snapdragon 870 處理器,安兔兔實測跑分達到 691,459 分。另外, Redmi K40 也配備最高 12GB LPDDR5 RAM 、256GB UFS 3.1 ROM 、Wi-Fi 6:

螢幕方面, Redmi K40 系列搭載 6.67 吋 FHD+(2400×1080)解析度的 E4 AMOLED 居中挖孔全螢幕,5,000,000:1 對比度、 1300nit、支持 DCI P-3 廣色域、HDR10+ 顯示、MEMC 動態補償等:

值得一提的是, Redmi K40 系列採用前置鏡頭挖孔直徑僅  2.76mm  也是目前全球最小:

Redmi K40 全系列正反面皆採用康寧第五代大猩猩玻璃保護:

另外, Redmi K40 系列也首次搭載 True Tone 顯示,能根據環境色溫自動調整顯示色溫:

Redmi K40 系列搭載環境光感應器,能自動感應亮度和色溫進行調節:

Redmi K40 系列也支持 120Hz 螢幕更新率:

這次 Redmi K40 系列則將觸控採樣率提升至 360Hz ,支持三指同時 360Hz 觸控採樣率: 

相機方面, Redmi K40 配備 4800 萬像素三鏡頭主相機,分別為 4800 萬像素主鏡頭(SONY IMX582 感光元件)、 800 萬像素 119° 超廣角鏡頭、 500 萬像素長焦微距鏡頭:

前相機部份 Redmi K40 則配備 2000 萬像素 AI 自然美顏相機,支持各項 AI 美顏功能:

電量方面, Redmi K40 內建 4520mAh 大容量電池並支持 33W 快速充電:

Redmi K40 系列機身重量僅 196g 、機身厚度也只有 7.8mm 相當輕薄:

對比目前市場上主流輕薄的手機, K40 系列的機身厚度真的相當輕薄:

另外, Redmi K40 全系列也採用側邊指紋辨識,可同時支持多功能智慧按鍵設定:

音效方面, Redmi K40 全系列配備立體聲雙揚聲器,通過 Hi-Res Audio、 Hi-Res Audio Wireless 認證,而且 Redmi K40 系列為 Redmi 首次支持 Dolby Atmos 杜比全景聲的機型:

其他方面,多功能 NFC、雙頻 GNSS 定位、紅外線遙控、IP53 等級生活防潑水、 X 軸線性馬達:

售價方面, Redmi K40 建議售價為人民幣 1,999 元(約合新台幣 8,645 元)起:

Redmi K40 共推出四種版本, 6GB+128GB 版本售價人民幣 1,999 元(約合新台幣 8,645 元)、8GB+128GB 版本售價人民幣 2,199 元(約合新台幣 9,510 元)、8GB+256GB 版本售價人民幣 2,499 元(約合新台幣 10,808 元)、12GB+256GB 售價人民幣 2699 元(約合新台幣 11,673 元): 

Redmi K40 Pro

Redmi K40 Pro 則是當初那款提前公佈起售價人民幣 2,999 元的高通 Snapdragon 888 旗艦手機,不過最終 Redmi 也在發表會帶來售價上的「驚喜」:

性能方面, Redmi K40 Pro 搭載 Qualcomm Snapdragon 888 處理器、配備最高 8GB LPDDR5 RAM 、 256GB ROM ,安兔兔性能跑分達到 744,877 分:

螢幕方面, Redmi K40 Pro 和 K40 標準版相同,皆搭載 6.67 吋 FHD+ 解析度 E4 AMOLED 螢幕,同樣支持 120Hz 螢幕更新率、360Hz 觸控採樣率、 DCI-P3 廣色域、5,000,000:1 和 TrueTone 環境色溫等:

Redmi K40 Pro 螢幕也通過 DisplayMate 最高 A+ 等級評價,不過 Redmi 品牌總經理盧偉冰表示 Redmi K40 標準版採用的螢幕也完全相同,只是差在 Redmi K40 Pro 有進行送測程序:

相機部份, Redmi K40 Pro 搭載 6400 萬像素三硬頭主相機,分別為 6400 萬像素主鏡頭、 800 萬像素 119° 超廣角鏡頭、 500 萬像素微距長焦鏡頭,前鏡頭則一樣配備 2000 萬像素 AI 美顏相機。 

電量方面, Redmi K40 Pro 一樣內建 4520mAh 大容量電池並支持 33W 快速充電:

網路連接方面, Redmi K40 Pro 系列則升級為  WiFi6e 規格:

Redmi K40 Pro 人民幣 2,799 元(約合新台幣 12,105 元)起:

Redmi K40 Pro 共推出三種規格配置, 6GB+128GB 版本售價人民幣 2,799 元(約合新台幣 12,105 元)、 8GB+128GB 版本售價人民幣 2,999 元(約合新台幣 12,970 元)、8GB+256GB 版本售價人民幣 3,299 元(約合新台幣 14,268 元):

Redmi K40 Pro+

除了 Redmi K40 和 Redmi K40 Pro ,今天也同步推出 Redmi K40 Pro+ 。不過 Redmi K40 Pro+ 在處理器、螢幕、電量、快充等規格幾乎和 Redmi K40 Pro 相同。

Redmi K40 Pro+ 在相機方面搭載 1.08 億像素三鏡頭主相機,分別為 1.08 億像素主鏡頭、800 萬像素 119° 超廣角鏡頭、 500 萬像素微距長焦鏡頭,前鏡頭則一樣配備 2000 萬像素 AI 美顏相機。 

主鏡頭也是 Redmi K40 Pro 與 K40 Pro + 規格上唯一的差異:

採用 1/1.52″ 的 Samsung ISOCELL HM2 感光元件:

Redmi K40 Pro+ 則推出 12GB+256GB 單一規格,建議售價人民幣 3,699 元(約合新台幣 15,999 元)。

配件方面, Redmi K40 全系列標配充電器、充電線、手機殼和出廠即貼上螢幕保護貼:

配件

在今日 Redmi K40 系列發表會也推出兩款專為遊戲玩家而生的遊戲週邊配件。首先是「大魔王肩鍵」能安裝在 Redmi K40 系列機身兩端延伸遊戲操控,售價為人民幣 59 元(約合新台幣 255 元):

另外,也推出一款「冰封電競背夾」,能在 1 分鐘降低 15°C 核心溫度,售價為人民幣 99 元(約合新台幣 428 元):

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

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

您也許會喜歡:

【推爆】終身$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維修中心

※超省錢租車方案

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

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

國外實測新款 Surface Pro X 的 SQ2 處理器效能幾乎跟 SQ1 一樣

微軟在去年十月時正式更新旗下 Surface Pro X 平版電腦,處理器升級至 SQ2,一樣是高通 ARM 架構,不過當時微軟並沒有特別說明這顆處理器的效能表現,而最近終於有外媒實測到實機,但讓人意外的是,跑分結果竟然跟 SQ1 差不多,也就是新款 Surface Pro X 效能幾乎沒有什麼提升。

新款 Surface Pro X 的 SQ2 處理器效能幾乎跟 SQ1 一樣

稍早一間德國媒體網站 Dr. Windows 分享 Surface Pro X 2020 年版的實測報告,共使用三款跑分軟體進行測試,並與舊款 Surface Pro X 比較,結果如下。

首先是 Jetstream 2 瀏覽器測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 獲得 89,614
  • 搭載 SQ2 處理器的 Surface Pro X 2020 獲得 90,664

安兔兔測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 獲得 286,740
  • 搭載 SQ2 處理器的 Surface Pro X 2020 獲得 286,271

Geekbench 4 測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 單核心獲得 3,530、多核心獲得 11,927
  • 搭載 SQ2 處理器的 Surface Pro X 2020 單核心獲得 3,627、多核心獲得 12,042

三個測試結果中,Jetstream 2 與 Geekbench 4 雖然新款確實比較高,但也只有多一點點,安兔兔反而變低。整體來看,幾乎可以說搭載 SQ2 處理器的 Surface Pro X 2020 效能並沒有提升。

另外最近微軟官方的韌體更新中,也都合併支援 SQ1 與 SQ2 處理器:

不確定為何這兩顆效能會這麼接近,但也顯示著,對於注重效能的朋友來說,舊款 Surface Pro X 或許是 CP 值更高的選擇。

當然,除了效能,Surface Pro X 2020 還是有地方提升,像續航力官方就承諾可達到 15 小時,比舊款多 2 小時。

無論如何,有在關注新款 Surface Pro X 2020 的人,建議等更多實測報告現身再做決定。

資料來源:Dr. Windows

微軟最新的廣告,直白地跟你說 Surface Pro 7 就是比 MacBook Pro 好

您也許會喜歡:

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

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

【其他文章推薦】

※回頭車貨運收費標準

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

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

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

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

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

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

Kubernetes-PV和PVC的原理和實踐

一、什麼是PV和PVC?

PV的全稱是Persistent Volume,翻譯過來為持久化存儲卷,是對底層的共享存儲的一種抽象,PV由管理員進行創建和配置,主要含存儲能力、訪問模式、存儲類型、回收策略、後端存儲類型等主要信息,它和具體的底層的共享存儲技術的實現方式有關,比如NFS、Hostpath、RBD等。

PVC的全稱是: PersistenVolumeClaim (持久化卷聲明),PVC是用戶存儲的一種聲明,PVC和Pod類似,Pod是消耗節點node資源,PVC消耗的是PV資源,Pod可以請求CPU的內存,而PVC可以請求特定的存儲空間和訪問模式。

二、PV和PVC的使用場景

 

 

 配圖來自K8S權威指南第四版

 

 存儲工程師把分佈式存儲系統上的總空間劃分成一個一個小的存儲塊,K8S的集群管理員將存儲塊和PV進行一一對應,用戶通過PVC對對存儲進行申請,比如可以指定具體容量的大小,訪問模式或者存儲類型,這樣的好處是用戶不需要關心底層的存儲實現細節,只需要直接申請使用PVC即可,若申請的PVC所對應的PV不能滿足用戶的要求,不會生效,直到有合適的PV生成,PVC會自動與PV完成綁定,存儲工程師、K8S管理員,用戶之間業務解耦,靈活性更強。

 

三、創建PV

PV支持多種不同類型的存儲,如:NFS、hostpath、RBD、ICCSI,本文以hostpath為例介紹如何創建PV

第一步:現在宿主機data目錄下data/pod/volume1,volume1將作為PV對應的hostpath本地存儲的目錄

第二步:通過yaml文件創建PV

 1 [root@k8s-master zhanglei]# cat pv-hostpath.yaml  2 kind: PersistentVolume #指定為PV類型  3 apiVersion: v1  4 metadata:  5   name: pv-statefulset #指定PV的名稱  6  labels: #指定PV的標籤  7  release: stable  8 spec:  9  capacity: 10     storage: 0.1Gi #指定PV的容量 11  accessModes: 12     - ReadWriteOnce #指定PV的訪問模式,簡寫為RWO,只支持掛在1個Pod的讀和寫 13  persistentVolumeReclaimPolicy: Recycle #指定PV的回收策略,Recycle表示支持回收,回收完成后支持再次利用 14  hostPath: #指定PV的存儲類型,本文是以hostpath為例 15     path: /data/pod/volume1 #指定PV對應後端存儲hostpath的目錄

說明:

 accessModes支持多種訪問模式

1)ReadWriteOnce(RWO):讀寫權限,但是只支持掛載在1個Pod

2)ReadOnlyMany(ROX):只讀權限,支持掛載在多個Pod

3)ReadWriteMany(RW):讀寫權限,支持掛載在多個Pod上

persistentVolumeReclaimPolicy的策略,指的是如果PVC被釋放掉后,PV的處理,這裏所說的釋放,指的是用戶刪除PVC后,與PVC對應的PV會被釋放掉,PVC個PV是一一對應的關係

1)Retain,PV的數據不會清理,會保留volume,如果需要清理,需要手動進行

2)Recycle,會將數據進行清理,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持),清理完成后,PV會呈available狀態,支持再次的bound

3)Delete,刪除存儲資源,會刪除PV及後端的存儲資源,比如刪除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)

四、創建PVC

[root@k8s-master zhanglei]# cat pvc-hostpath.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mppvc-01                  # 指定PVC的名稱
  namespace: default
spec:
 accessModes: ["ReadWriteOnce"]    # 指定PVC的訪問模式
 resources:
   requests: 
     storage: 0.05Gi               # PVC申請的容量

說明:

1)PVC聲明了accessModes訪問類型為ReadWriteOnce,創建后,系統會自動去找能夠支持ReadWriteOnce訪問類型的PV,若無符合條件的PV,則不會進行綁定,

2)PVC聲明了storage的大小為0.05Gi,創建后,系統會自動去找能夠支持此容量的PV,通常PV的容量至少要大於或者等於0.05Gi才會去進行綁定

從這裏來看,對於用戶來說,即只需要聲明訪問類型、容量、另外還可通過StorageClass聲明具體的PV類型即可完成對持久化存儲卷的申請,而不需要去維護和關注後端存儲

五、查詢PV和PVC的

經過前面的步驟我們創建了PV和PVC,現在來看下兩者是否已經完成了綁定,在PV的創建已經指定了其名稱為pv-statefulset,PVC的名稱為mppvc-01

 

 

[root@k8s-master zhanglei]# kubectl get pv |grep pv-statefulset
pv-statefulset      107374182400m   RWO            Recycle          Bound    default/mppvc-01 

 

[root@k8s-master zhanglei]# kubectl get pvc |grep mppvc-01
mppvc-01                            Bound    pv-statefulset      107374182400m   RWO                           13d

可以看到pv-statefulset這個PV已經和mppvc-01的PVC進行了綁定(Bound),RWO和Recycle也是之前PV和PVC聲明的狀態,說明綁定成功


[root@k8s-master zhanglei]# kubectl describe pv pv-statefulset
Name:            pv-statefulset
Labels:          release=stable
Annotations:     pv.kubernetes.io/bound-by-controller: yes
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    
Status:          Bound
Claim:           default/mppvc-01
Reclaim Policy:  Recycle
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        107374182400m
Node Affinity:   <none>
Message:         
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /data/pod/volume1
    HostPathType:  
Events:            <none>
[root@k8s-master zhanglei]# kubectl describe pvc mppvc-01
Name:          mppvc-01
Namespace:     default
StorageClass:  
Status:        Bound
Volume:        pv-statefulset
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      107374182400m
Access Modes:  RWO
VolumeMode:    Filesystem
Mounted By:    <none>
Events:        <none>

再來看下PV的詳細(describe)信息,可以看到type是hostpath類型,显示了數據卷在宿主機的/data/pod/volume1的目錄。

 

六、總結

創建PV和PVC分為二步:

第一步:創建PV,支持自定義不同的存儲大小和訪問模式(RWX,RWO)、存放路徑、後端服務server(如hostpath、或NAS盤的數據盤的掛載點)

第二步:創建PVC,綁定到PV。創建PVC的時候可以指定PVC的request storage,即申請的存儲的容量,會根據申請的storage和訪問模式自動匹配符合要求的PV

創建完PV和PVC主要是為了使用它來達到實現持久化存儲的目的,如何進行使用請看本作者後續與statufulset有關的文章,謝謝閱讀~

 

作者簡介:雲計算容器\K8S方向產品經理,學點技術,為更好地設計產品。

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

【其他文章推薦】

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

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

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

※超省錢租車方案

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

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